Spring 3引入了core.convert
包提供了通用的类型转化系统。系统定义了一个SPI来实现类型转换的逻辑,和一个API在运行时执行类型转换。在Spring容器中,这个系统可以被用来代替PropertyEditors
将表示bean属性的字符串转换成要求的属性类型。这个公开的API也可以在你的应用程序需要类型转换的任何地方使用。
SPI实现的类型转换逻辑是简单且强类型的:
package org.springframework.core.convert.converter;
public interface Converter<S, T> {
T convert(S source);
}
为了创建你自己的转换器,需要简单的实现上述的接口,泛型参数S
是你需要转换的类型,而T
是你转换成的类型。如果是S
集合或者是数组要被转换成T
的集合或者数组的话,这样的转换器可以很明了的被应用,只要提供的数组/集合转换器也被注册(DefaultConversionService
默认这么做了)。
每次调用convert(S)
时,需要保证参数不能为null。你的转换器在转换失败的时候可以抛出任何未检查的一场;特别的,IllegalArgumentException
应该被抛出来报告非法的输入值。注意确保你的Converter
实现是线程安全的。
为了方便起见,许多转换器的实现已经在core.convert.support
包被提供。包括了将字符串转成数字或是其他常见的类型。将StringToInteger
作为一个典型的Converter
实现的例子:
package org.springframework.core.convert.support;
final class StringToInteger implements Converter<String, Integer> {
public Integer convert(String source) {
return Integer.valueOf(source);
}
}
当你需要集中整个类层次结构的转换逻辑时,比如,将String
转换成java.lang.Enum
对象,实现ConverterFactory
:
package org.springframework.core.convert.converter;
public interface ConverterFactory<S, R> {
<T extends R> Converter<S, T> getConverter(Class<T> targetType);
}
泛型参数S
是你要转换的类型,R是你转换成的类的基础类。然后实现getConverter(Class<T>)
,T是R的子类。
参考StringToEnum
ConverterFactory作为例子:
package org.springframework.core.convert.support;
final class StringToEnumConverterFactory implements ConverterFactory<String, Enum> {
public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToEnumConverter(targetType);
}
private final class StringToEnumConverter<T extends Enum> implements Converter<String, T> {
private Class<T> enumType;
public StringToEnumConverter(Class<T> enumType) {
this.enumType = enumType;
}
public T convert(String source) {
return (T) Enum.valueOf(this.enumType, source.trim());
}
}
}
当你需要一个复杂的转换器实现时,请考虑GenericConverter
接口。GenericConverter
支持多种源类型到目的类型的转换,更灵活且更少的强类型签名。另外,GenericConveter
还能够在你实现的转换逻辑中提供使用的源和目标字段的上下文。这种上下可以让类型转换由字段注释或者是字段签名上声明的通用信息来驱动。
package org.springframework.core.convert.converter;
public interface GenericConverter {
public Set<ConvertiblePair> getConvertibleTypes();
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
为了实现GernericConveter
,要实现getConvertibleTypes()
方法,该方法返回了所支持的源类型到目标类型的键值对。然后根据你的转换逻辑实现convert(Object, TypeDescriptor, targetType)
信息。源TypeDescriptor
提供了对拥有的要被转换的值的访问。目标TypeDecriptor
提供了要转成的字段值的访问。
GeneriConverter
的一个好例子一个是数组和容器的转换器。这样的ArrayToCollectionConverter
内省声明了目标容器的类型来接写容器的元素类型。这允许数组中的每个元素在成为目标容器之前先转换成元素的类型。
由于
GenericConveter
是个更复杂的SPI,只有在你需要的时候再使用它。基本的类型转换需求则选择Conveter
或是ConverterFactory
。
有时候,你需要一个Conveter
在某个条件为真时去执行。比如,你可能只有在目标字段有某个特殊的注释时,才会去执行Conveter
。或者你可能在目标类型中定义了某个特殊的方法,比如static valueOf
时才执行。ConditionalGenericConverter
联合了GenericConverter
和ConditionalConverter
接口,允许你定义自己的符合调节:
public interface ConditionalConverter {
boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType);
}
public interface ConditionalGenericConverter
extends GenericConverter, ConditionalConverter {
}
ConditionalGenericConveter
的一个好例子是一个EntityConverter,用来转换持久化的实体标识符和实体的引用。这样的EntityConverty只有在目标实体类声明了静态的finder方法(比如,findAccount(Long)
)才符合条件。你可以在matches(TypeDescriptor, TypeDescriptor)
的实现中执行finder方法的检查。
ConversionService
定义在运行时执行类型转换逻辑的统一API。转换器通常在这个接口之下执行:
package org.springframework.core.convert;
public interface ConversionService {
boolean canConvert(Class<?> sourceType, Class<?> targetType);
<T> T convert(Object source, Class<T> targetType);
boolean canConvert(TypeDescriptor sourceType, TypeDescriptor targetType);
Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType);
}
大部分的ConversionService实现也实现了ConverterRegistry
,它提供了一个SPI来自注册的转换器。在内部,ConversionService实现是将类型转换的逻辑委托给其注册的转换器。
core.convert,support
包中提供了健壮的ConversionService实现。GenericConversionService
是适用于大多数环境的通用实现。ConversionServiceFactory
提供了一个便利的工厂去创建普通的ConversionService配置。
ConversionSerive是一个无状态对象,被设计在应用程序启动时初始化,然后在多个线程间共享。在一个Spring应用中,你可以为每个Spring容器(或是一个应用程序上下文)配置一个ConversionService实例。ConversionService将会被Spring检索然后在任何需要类型转化你的时候被框架执行。你也可以直接将ConversionService注入到你的Bean中然后调用。
如果Spring中没有注册ConversionService,那么原始的基于PropertyEditor的系统将会被使用。
为了在Spring中注册一个默认的ConversionService,要添加下面这个id为conversionService
的bean定义:
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean"/>
会有一个默认的ConversionService可以在字符串,数字,枚举,容器,map和其他常用类型之间转换类型。为了用你自定义的转换器(们)补充或是覆盖默认转换器,设置converters
属性分。属性可以是Converter
,或者ConverterFactory
或者GenericConverter
接口。
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="example.MyCustomConverter"/>
</set>
</property>
</bean>
在Spring MVC应用中使用ConversionService也是很常见的。请在Spring MVC那章中,看21.16.3节,"Conversion and Formatting"。在某些情况下,你可能希望在转换过程中应用格式化。请参考8.6.3节,"FormatterRegistry SPI"获得使用FormattingConversionServiceFactoryBean
的细节。
为了用编程的方式使用ConversionService实例,可以像注入其他Bean一样注入ConversionService引用:
@Service
public class MyService {
@Autowired
public MyService(ConversionService conversionService) {
this.conversionService = conversionService;
}
public void doIt() {
this.conversionService.convert(...)
}
}
大多数使用情况,可以使用特定的targetType的convert方法,但是不适合像参数化元素集合这种更富在的类型。比如,如果你想要将一个Integer
的List
转换成String
的List,那需要你提供一个更正式的关于源和目标类型的定义。
幸运的是,TypeDescriptor
提供了几个选像让这变得直接:
DefaultConversionService cs = new DefaultConversionService();
List<Integer> input = ....
cs.convert(input,
TypeDescriptor.forObject(input), // List<Integer> type descriptor
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)));
注意,DefaultConversionService
自动注册了适用于大多数环境的转换器。这包括了容集合转换器,纯量转换器,和基本的Object
到String
的转换器。可以使用DefualtConversionService
类的静态的addDefaultConverters
方法将同类的转换器注册到ConverterRegistry
。
值类型的转换器会被数组和集合重用。因此,假设,标准的集合处理是适当的,那么不需要创建一个特殊的转换器来转换S类型的集合到T类型的集合。